
// 封装监听目标元素进入视口的通用逻辑

import { ref, computed } from 'vue'
import { useIntersectionObserver, useIntervalFn } from '@vueuse/core'
import dayjs from 'dayjs'

// 逻辑复用 就是找到重复代码的地方 再找到可变的地方 把可变的东西抽象成一个参数
// setup函数组合的永远是响应式的状态数据

/**
 * 组件数据懒加载
 *  1.解决的问题
 *    网络层面的优化 只有组件进入视口才会获取数据 (渲染层面优化)
 *
 *  2.实现思路
 *    监听组件是否已经进入到视口区域 在进入之后发送ajax
 *
 *  3.技术手段
 *    useIntersectionObserver
 *    对目标元素进行监听  然后满足条件执行回调  回调中就可以发送ajax
 *
 *  4.注意点
 *    1. target ref对象
 *    2. 正常情况下 每次进入视口移出视口都会再次执行回调
 *    3. 正常逻辑下 不应执行多次  可以使用stop在执行了首次加载之后 停止监听
 */

export function useObserver (apiFn) {
  // 监听的目标对象
  const target = ref(null) // 获取组件实例
  // console.log('当前传入的target参数', target) // 报错时 target拿到的是文本节点 不是元素节点
  // 1. stop 可执行方法，用来停止对于目标元素的监听, 不管元素进入还是离开视口区域都不会再执行回调函数
  // 2. target 要监听的对象 dom对象或者组件实例对象
  // 3. isIntersecting 布尔值，代表当前舰艇的元素对象是否进入视口区， 进来就为 true
  const { stop } = useIntersectionObserver(
    target, // 监听的目标元素
    ([{ isIntersecting }], observerElement) => {
      // console.log(isIntersecting)
      // isIntersecting  判断目标元素是否进入视口区
      // 可以使用 isIntersecting 变量的是否为true来控制ajax请求是否要发起
      if (isIntersecting) {
        apiFn() // 发送请求获取数据
        stop() // 请求数据完毕停止监听
      }
    },
    {
      // threshold: 用来控制目标target进入视口区域的比例
      //  0 - 1  数值越大要求进入到视口区的面积越大, 才会触发回调
      //  0 - 1  数值越小要求进入到视口区的面积越小, 才会触发回调
      threshold: 0.1
    }
  )
  return { target }
}

// 根据需求定义函数架子
// 需要的参数 返回什么功能  初步定义功能函数
// 核心逻辑：每隔一秒钟 对countTime减1 (定时器中写逻辑)
// 修改：
// 1. 把接口传过来的秒数 转换成 xx分 xx秒
// 2. 在订单组件进行倒计时时，初始值不是一开始就知道的，而是通过接口传过来的
// 需要修改为 调用start的时候 传初始值
export function useCountDown (_time) {
  // const timer = setInterval(() => {
  //   // console.log('自定义定时器')
  // }, 1000)
  // 未经过额外清除的自定义的定时器 在组件销毁之后依旧常驻内存 需要我们手动清除
  // 清除定时器的逻辑放到 组件销毁的生命周期函数中  vue2中 destroyed函数中
  // onUnmounted(() => {
  //   clearInterval(timer)
  // })
  const countTime = ref(0)
  // 基于某个现有数据进行格式化操作 计算属性
  const countTimeText = computed(() => {
    return dayjs.unix(countTime.value).format('mm分ss秒')
  })
  // resume 可执行函数 只要执行这个函数  自己的逻辑就会每隔一秒执行一次
  // pause 可执行函数  只要执行这个函数  自己的逻辑就立即暂停
  const { resume, pause } = useIntervalFn(() => {
    // your function
    if (countTime.value === 0) {
      pause()
    } else {
      countTime.value--
    }
    // console.log('封装的定时器')
  }, 1000)

  function start (time) {
    // 开始倒计时逻辑
    countTime.value = time || _time
    resume()
  }

  return {
    countTime, // 当前计数的数字
    countTimeText,
    start, // start 方法 开始倒计时逻辑
    pause // pause方法  暂停倒计时
  }
}

// 完成一个功能函数
// 1. 先按照使用的方式把函数的入参 和 return 值都定义好
// 2. 想清楚核心逻辑：每隔一秒钟 对数据做一个自减操作 定时器 回调中做自减逻辑
// 3. useIntervalFn  可以提供 ‘每隔一秒钟执行一个你自己逻辑’ 的定时器 并且给到两个方法 pause和resume

// 总结：
// 1. 现有的函数 不能满足需求
// 原因：
// (1) 初始值和之前使用场景不同 不是一上来就有的而是通过接口获取到的
// (2) 所有抽离出去的组合函 在组合回来之后必须在setup函数中调用 不能在接口回调中执行 原因：拿不到useCountDown返回的数据

// 2. 修正函数
// 核心思路：把初始值的传递 不交给useCountDown 交给start函数 start函数的执行才正式开始倒计时, 可以在任何时机下调用,包括接口返回之后, useCountDown 只负责产出响应式数据countTime, 而响应式数据的初始化操作交给start方法

// 问题：修改一个函数之后，之前根据原来定义方法的业务使用会报错 解决方案
// 方案一：区业务组件中修改(使用频率低的情况下)
// 方案二：在当前函数中做容错处理
